<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图形编辑器 - 带连线功能</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            display: flex;
            flex-direction: column;
            height: 100vh;
        }

        .header {
            background-color: #333;
            color: white;
            padding: 10px;
            text-align: center;
        }

        .container {
            display: flex;
            flex: 1;
            overflow: hidden;
        }

        .toolbar {
            width: 150px;
            background-color: #f0f0f0;
            padding: 10px;
            border-right: 1px solid #ccc;
        }

        .shape-item {
            width: 100px;
            height: 50px;
            margin: 10px auto;
            display: flex;
            justify-content: center;
            align-items: center;
            border: 1px dashed #999;
            cursor: move;
            background-color: white;
            user-select: none;
        }

        .editor {
            flex: 1;
            position: relative;
            background-color: #fff;
            overflow: hidden;
        }

        .editor-content {
            width: 100%;
            height: 100%;
            position: relative;
            background-image: linear-gradient(#eee 1px, transparent 1px),
            linear-gradient(90deg, #eee 1px, transparent 1px);
            background-size: 20px 20px;
        }

        .shape {
            position: absolute;
            display: flex;
            justify-content: center;
            align-items: center;
            border: 2px solid #333;
            cursor: move;
            user-select: none;
        }

        .rectangle {
            background-color: rgba(100, 149, 237, 0.5);
        }

        .circle {
            border-radius: 50%;
            background-color: rgba(144, 238, 144, 0.5);
        }

        .triangle {
            width: 0;
            height: 0;
            border-left: 30px solid transparent;
            border-right: 30px solid transparent;
            border-bottom: 60px solid rgba(255, 165, 0, 0.5);
            background-color: transparent;
        }

        .shape-label {
            pointer-events: none;
            font-size: 12px;
            text-align: center;
        }

        .selected {
            border: 2px dashed #ff0000;
            box-shadow: 0 0 10px rgba(255, 0, 0, 0.5);
        }

        .resize-handle {
            position: absolute;
            width: 8px;
            height: 8px;
            background-color: #fff;
            border: 1px solid #333;
            z-index: 10;
        }

        .resize-nw {
            top: -4px;
            left: -4px;
            cursor: nw-resize;
        }

        .resize-ne {
            top: -4px;
            right: -4px;
            cursor: ne-resize;
        }

        .resize-sw {
            bottom: -4px;
            left: -4px;
            cursor: sw-resize;
        }

        .resize-se {
            bottom: -4px;
            right: -4px;
            cursor: se-resize;
        }

        /* 连线相关样式 */
        .connection-point {
            position: absolute;
            width: 10px;
            height: 10px;
            background-color: #ff5722;
            border-radius: 50%;
            cursor: pointer;
            z-index: 5;
        }

        .connection-line {
            position: absolute;
            height: 2px;
            background-color: #333;
            transform-origin: left center;
            z-index: 1;
        }

        .connection-arrow {
            position: absolute;
            width: 0;
            height: 0;
            border-left: 6px solid transparent;
            border-right: 6px solid transparent;
            border-top: 10px solid #333;
            transform-origin: center bottom;
            z-index: 2;
        }

        .mode-selector {
            position: absolute;
            top: 10px;
            right: 10px;
            z-index: 100;
            background: white;
            padding: 5px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
    </style>
</head>
<body>
<div class="header">
    <h2>图形编辑器 - 带连线功能</h2>
</div>

<div class="container">
    <div class="toolbar" id="toolbar">
        <h3>图形工具栏</h3>
        <div class="shape-item" draggable="true" data-type="rectangle">矩形</div>
        <div class="shape-item" draggable="true" data-type="circle">圆形</div>
        <div class="shape-item" draggable="true" data-type="triangle">三角形</div>
    </div>

    <div class="editor">
        <div class="editor-content" id="editor"></div>
    </div>
</div>

<div class="mode-selector">
    <label>
        <input type="radio" name="mode" value="select" checked> 选择模式
    </label>
    <label>
        <input type="radio" name="mode" value="connect"> 连线模式
    </label>
</div>

<script>
    document.addEventListener('DOMContentLoaded', function() {
        const editor = document.getElementById('editor');
        const toolbar = document.getElementById('toolbar');
        const modeSelector = document.querySelector('.mode-selector');

        let selectedShape = null;
        let isDragging = false;
        let startX, startY;
        let offsetX, offsetY;
        let currentResizeHandle = null;

        // 连线相关变量
        let isConnectionMode = false;
        let connectionStartPoint = null;
        let tempLine = null;
        let connections = []; // 存储所有连线信息

        // 模式切换
        modeSelector.addEventListener('change', function(e) {
            isConnectionMode = (e.target.value === 'connect');
            if (!isConnectionMode && tempLine) {
                editor.removeChild(tempLine);
                tempLine = null;
                connectionStartPoint = null;
            }
        });

        // 为工具栏中的图形添加拖拽事件
        document.querySelectorAll('.shape-item').forEach(item => {
            item.addEventListener('dragstart', function(e) {
                e.dataTransfer.setData('text/plain', this.getAttribute('data-type'));
                e.dataTransfer.effectAllowed = 'copy';
            });
        });

        // 编辑器区域拖放事件
        editor.addEventListener('dragover', function(e) {
            e.preventDefault();
            e.dataTransfer.dropEffect = 'copy';
        });

        editor.addEventListener('drop', function(e) {
            if (isConnectionMode) return;

            e.preventDefault();
            const type = e.dataTransfer.getData('text/plain');
            if (type) {
                createShape(type, e.clientX - editor.getBoundingClientRect().left,
                    e.clientY - editor.getBoundingClientRect().top);
            }
        });

        // 创建图形
        function createShape(type, x, y) {
            const shape = document.createElement('div');
            shape.className = `shape ${type}`;

            // 设置初始位置和大小
            shape.style.left = `${x}px`;
            shape.style.top = `${y}px`;

            // 不同类型图形的默认尺寸
            switch(type) {
                case 'rectangle':
                    shape.style.width = '100px';
                    shape.style.height = '60px';
                    break;
                case 'circle':
                    shape.style.width = '80px';
                    shape.style.height = '80px';
                    break;
                case 'triangle':
                    shape.style.width = '60px';
                    shape.style.height = '60px';
                    break;
            }

            // 添加图形标签
            const label = document.createElement('div');
            label.className = 'shape-label';
            label.textContent = type;
            shape.appendChild(label);

            // 添加图形到编辑器
            editor.appendChild(shape);

            // 添加连接点
            addConnectionPoints(shape);

            // 添加选择功能
            shape.addEventListener('mousedown', function(e) {
                if (isConnectionMode) {
                    handleConnectionStart(this, e);
                } else {
                    e.stopPropagation();
                    selectShape(this, e);
                }
            });

            // 添加调整大小控制点
            if (type !== 'triangle') { // 三角形不支持调整大小
                addResizeHandles(shape);
            }

            return shape;
        }

        // 添加连接点
        function addConnectionPoints(shape) {
            const positions = [
                { x: '50%', y: '0%', name: 'top' },
                { x: '100%', y: '50%', name: 'right' },
                { x: '50%', y: '100%', name: 'bottom' },
                { x: '0%', y: '50%', name: 'left' }
            ];

            positions.forEach(pos => {
                const point = document.createElement('div');
                point.className = 'connection-point';
                point.style.left = pos.x;
                point.style.top = pos.y;
                point.style.transform = 'translate(-50%, -50%)';
                point.dataset.position = pos.name;
                point.dataset.parentId = shape.id || (shape.id = 'shape-' + Math.random().toString(36).substr(2, 9));

                point.addEventListener('mousedown', function(e) {
                    if (isConnectionMode) {
                        e.stopPropagation();
                        handleConnectionStart(shape, e, this);
                    }
                });

                shape.appendChild(point);
            });
        }

        // 处理连线开始
        function handleConnectionStart(shape, e, point) {
            if (connectionStartPoint) return; // 已经有一个起点

            const rect = shape.getBoundingClientRect();
            const editorRect = editor.getBoundingClientRect();

            // 计算起点位置
            let startX, startY;
            if (point) {
                // 从连接点开始
                const pointRect = point.getBoundingClientRect();
                startX = pointRect.left + pointRect.width/2 - editorRect.left;
                startY = pointRect.top + pointRect.height/2 - editorRect.top;
            } else {
                // 从图形中心开始
                startX = rect.left + rect.width/2 - editorRect.left;
                startY = rect.top + rect.height/2 - editorRect.top;
            }

            connectionStartPoint = {
                shape: shape,
                point: point,
                x: startX,
                y: startY
            };

            // 创建临时连线
            tempLine = document.createElement('div');
            tempLine.className = 'connection-line';
            tempLine.style.left = `${startX}px`;
            tempLine.style.top = `${startY}px`;
            editor.appendChild(tempLine);

            // 监听鼠标移动来更新临时连线
            document.addEventListener('mousemove', updateTempConnection);
            document.addEventListener('mouseup', finishConnection);
        }

        // 更新临时连线
        function updateTempConnection(e) {
            if (!tempLine || !connectionStartPoint) return;

            const editorRect = editor.getBoundingClientRect();
            const endX = e.clientX - editorRect.left;
            const endY = e.clientY - editorRect.top;

            // 计算连线的长度和角度
            const dx = endX - connectionStartPoint.x;
            const dy = endY - connectionStartPoint.y;
            const length = Math.sqrt(dx * dx + dy * dy);
            const angle = Math.atan2(dy, dx) * 180 / Math.PI;

            // 更新连线样式
            tempLine.style.width = `${length}px`;
            tempLine.style.transform = `rotate(${angle}deg)`;
        }

        // 完成连线
        function finishConnection(e) {
            if (!tempLine || !connectionStartPoint) return;

            // 移除事件监听
            document.removeEventListener('mousemove', updateTempConnection);
            document.removeEventListener('mouseup', finishConnection);

            const editorRect = editor.getBoundingClientRect();
            const endX = e.clientX - editorRect.left;
            const endY = e.clientY - editorRect.top;

            // 检查是否连接到另一个图形
            const elements = document.elementsFromPoint(e.clientX, e.clientY);
            let endShape = null;
            let endPoint = null;

            for (let el of elements) {
                if (el.classList.contains('connection-point')) {
                    endPoint = el;
                    endShape = el.parentElement;
                    break;
                } else if (el.classList.contains('shape') && el !== connectionStartPoint.shape) {
                    endShape = el;
                    break;
                }
            }

            if (endShape) {
                // 计算终点位置
                let endPosX, endPosY;
                if (endPoint) {
                    const pointRect = endPoint.getBoundingClientRect();
                    endPosX = pointRect.left + pointRect.width/2 - editorRect.left;
                    endPosY = pointRect.top + pointRect.height/2 - editorRect.top;
                } else {
                    const shapeRect = endShape.getBoundingClientRect();
                    endPosX = shapeRect.left + shapeRect.width/2 - editorRect.left;
                    endPosY = shapeRect.top + shapeRect.height/2 - editorRect.top;
                }

                // 创建永久连线
                createPermanentConnection(
                    connectionStartPoint.shape, connectionStartPoint.point,
                    endShape, endPoint,
                    connectionStartPoint.x, connectionStartPoint.y,
                    endPosX, endPosY
                );
            }

            // 清除临时连线
            editor.removeChild(tempLine);
            tempLine = null;
            connectionStartPoint = null;
        }

        // 创建永久连线
        function createPermanentConnection(startShape, startPoint, endShape, endPoint, startX, startY, endX, endY) {
            // 计算连线的长度和角度
            const dx = endX - startX;
            const dy = endY - startY;
            const length = Math.sqrt(dx * dx + dy * dy);
            const angle = Math.atan2(dy, dx) * 180 / Math.PI;

            // 创建连线元素
            const line = document.createElement('div');
            line.className = 'connection-line';
            line.style.left = `${startX}px`;
            line.style.top = `${startY}px`;
            line.style.width = `${length}px`;
            line.style.transform = `rotate(${angle}deg)`;

            // 创建箭头
            const arrow = document.createElement('div');
            arrow.className = 'connection-arrow';
            arrow.style.left = `${endX}px`;
            arrow.style.top = `${endY}px`;
            arrow.style.transform = `translate(-50%, -50%) rotate(${angle}deg)`;

            editor.appendChild(line);
            editor.appendChild(arrow);

            // 存储连线信息
            const connection = {
                id: 'conn-' + Math.random().toString(36).substr(2, 9),
                startShape: startShape.id,
                startPoint: startPoint ? startPoint.dataset.position : null,
                endShape: endShape.id,
                endPoint: endPoint ? endPoint.dataset.position : null,
                lineElement: line,
                arrowElement: arrow
            };

            connections.push(connection);

            // 当图形移动时更新连线
            const updateConnection = () => {
                updateConnectionPosition(connection);
            };

            startShape.addEventListener('move', updateConnection);
            if (endShape) endShape.addEventListener('move', updateConnection);
        }

        // 更新连线位置
        function updateConnectionPosition(connection) {
            const startShape = document.getElementById(connection.startShape);
            const endShape = document.getElementById(connection.endShape);
            if (!startShape || !endShape) return;

            const editorRect = editor.getBoundingClientRect();

            // 获取起点位置
            let startX, startY;
            if (connection.startPoint) {
                const point = startShape.querySelector(`.connection-point[data-position="${connection.startPoint}"]`);
                if (point) {
                    const pointRect = point.getBoundingClientRect();
                    startX = pointRect.left + pointRect.width/2 - editorRect.left;
                    startY = pointRect.top + pointRect.height/2 - editorRect.top;
                }
            }
            if (startX === undefined) {
                const shapeRect = startShape.getBoundingClientRect();
                startX = shapeRect.left + shapeRect.width/2 - editorRect.left;
                startY = shapeRect.top + shapeRect.height/2 - editorRect.top;
            }

            // 获取终点位置
            let endX, endY;
            if (connection.endPoint) {
                const point = endShape.querySelector(`.connection-point[data-position="${connection.endPoint}"]`);
                if (point) {
                    const pointRect = point.getBoundingClientRect();
                    endX = pointRect.left + pointRect.width/2 - editorRect.left;
                    endY = pointRect.top + pointRect.height/2 - editorRect.top;
                }
            }
            if (endX === undefined) {
                const shapeRect = endShape.getBoundingClientRect();
                endX = shapeRect.left + shapeRect.width/2 - editorRect.left;
                endY = shapeRect.top + shapeRect.height/2 - editorRect.top;
            }

            // 计算连线的长度和角度
            const dx = endX - startX;
            const dy = endY - startY;
            const length = Math.sqrt(dx * dx + dy * dy);
            const angle = Math.atan2(dy, dx) * 180 / Math.PI;

            // 更新连线样式
            connection.lineElement.style.left = `${startX}px`;
            connection.lineElement.style.top = `${startY}px`;
            connection.lineElement.style.width = `${length}px`;
            connection.lineElement.style.transform = `rotate(${angle}deg)`;

            // 更新箭头样式
            connection.arrowElement.style.left = `${endX}px`;
            connection.arrowElement.style.top = `${endY}px`;
            connection.arrowElement.style.transform = `translate(-50%, -50%) rotate(${angle}deg)`;
        }

        // 选择图形
        function selectShape(shape, e) {
            // 取消之前选中的图形
            if (selectedShape) {
                selectedShape.classList.remove('selected');
                const handles = selectedShape.querySelectorAll('.resize-handle');
                handles.forEach(handle => handle.style.display = 'none');
            }

            // 选中新图形
            selectedShape = shape;
            shape.classList.add('selected');

            // 显示调整大小控制点
            const handles = shape.querySelectorAll('.resize-handle');
            handles.forEach(handle => handle.style.display = 'block');

            // 准备拖动
            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;

            const rect = shape.getBoundingClientRect();
            offsetX = startX - rect.left;
            offsetY = startY - rect.top;

            document.addEventListener('mousemove', moveShape);
            document.addEventListener('mouseup', stopDrag);
        }

        // 移动图形
        function moveShape(e) {
            if (!isDragging) return;

            if (currentResizeHandle) {
                // 调整大小逻辑
                resizeShape(e);
            } else {
                // 移动图形逻辑
                const editorRect = editor.getBoundingClientRect();
                let newX = e.clientX - offsetX - editorRect.left;
                let newY = e.clientY - offsetY - editorRect.top;

                // 限制在编辑器范围内
                newX = Math.max(0, Math.min(newX, editorRect.width - selectedShape.offsetWidth));
                newY = Math.max(0, Math.min(newY, editorRect.height - selectedShape.offsetHeight));

                selectedShape.style.left = `${newX}px`;
                selectedShape.style.top = `${newY}px`;

                // 触发自定义移动事件，用于更新连线
                const moveEvent = new CustomEvent('move');
                selectedShape.dispatchEvent(moveEvent);
            }
        }

        // 停止拖动
        function stopDrag() {
            isDragging = false;
            currentResizeHandle = null;
            document.removeEventListener('mousemove', moveShape);
            document.removeEventListener('mouseup', stopDrag);
        }

        // 添加调整大小控制点
        function addResizeHandles(shape) {
            const positions = ['nw', 'ne', 'sw', 'se'];

            positions.forEach(pos => {
                const handle = document.createElement('div');
                handle.className = `resize-handle resize-${pos}`;
                handle.style.display = 'none';

                handle.addEventListener('mousedown', function(e) {
                    e.stopPropagation();
                    currentResizeHandle = pos;
                    startX = e.clientX;
                    startY = e.clientY;

                    const rect = shape.getBoundingClientRect();
                    offsetX = startX - rect.left;
                    offsetY = startY - rect.top;

                    document.addEventListener('mousemove', moveShape);
                    document.addEventListener('mouseup', stopDrag);
                });

                shape.appendChild(handle);
            });
        }

        // 调整图形大小
        function resizeShape(e) {
            const dx = e.clientX - startX;
            const dy = e.clientY - startY;

            const rect = selectedShape.getBoundingClientRect();
            const editorRect = editor.getBoundingClientRect();

            let newWidth = rect.width;
            let newHeight = rect.height;
            let newLeft = rect.left - editorRect.left;
            let newTop = rect.top - editorRect.top;

            switch(currentResizeHandle) {
                case 'nw':
                    newWidth = Math.max(30, rect.width - dx);
                    newHeight = Math.max(30, rect.height - dy);
                    newLeft = Math.max(0, rect.left - editorRect.left + dx);
                    newTop = Math.max(0, rect.top - editorRect.top + dy);
                    break;
                case 'ne':
                    newWidth = Math.max(30, rect.width + dx);
                    newHeight = Math.max(30, rect.height - dy);
                    newTop = Math.max(0, rect.top - editorRect.top + dy);
                    break;
                case 'sw':
                    newWidth = Math.max(30, rect.width - dx);
                    newHeight = Math.max(30, rect.height + dy);
                    newLeft = Math.max(0, rect.left - editorRect.left + dx);
                    break;
                case 'se':
                    newWidth = Math.max(30, rect.width + dx);
                    newHeight = Math.max(30, rect.height + dy);
                    break;
            }

            // 限制最大尺寸不超过编辑器
            newWidth = Math.min(newWidth, editorRect.width - newLeft);
            newHeight = Math.min(newHeight, editorRect.height - newTop);

            selectedShape.style.width = `${newWidth}px`;
            selectedShape.style.height = `${newHeight}px`;
            selectedShape.style.left = `${newLeft}px`;
            selectedShape.style.top = `${newTop}px`;

            // 触发自定义移动事件，用于更新连线
            const moveEvent = new CustomEvent('move');
            selectedShape.dispatchEvent(moveEvent);

            startX = e.clientX;
            startY = e.clientY;
        }

        // 点击编辑器空白处取消选择
        editor.addEventListener('mousedown', function(e) {
            if (e.target === editor) {
                if (selectedShape) {
                    selectedShape.classList.remove('selected');
                    const handles = selectedShape.querySelectorAll('.resize-handle');
                    handles.forEach(handle => handle.style.display = 'none');
                    selectedShape = null;
                }
            }
        });
    });
</script>
</body>
</html>